import OrgIcons from '@/components/Common/GlobalComps/orgIcons';
import { MessageType } from '@/ts/core';
import { Button, Input, Layout, Menu, message, Select, Space, Spin } from 'antd';
import { Header, Content, Footer } from 'antd/lib/layout/layout';
import Sider from 'antd/lib/layout/Sider';
import React, { ReactNode, useEffect, useLayoutEffect, useRef, useState } from 'react';
import cls from './index.module.less';
import { command, common, schema } from '@/ts/base';
import {
  IAgent,
  IAgentManager,
  IUpdatableMessage,
  StreamResponse,
} from '@/ts/core/chat/agent';
import { parseMsg } from '../chat/components/parseMsg';
import { CloseSquareOutlined, LoadingOutlined } from '@ant-design/icons';
import { BodyContent, TokenStatus } from '@/ts/core/chat/agent/aiModel';

interface IProps {
  manager: IAgentManager;
}

const AiAgent: React.FC<IProps> = (props) => {
  return (
    <Layout className={cls.agentLayout}>
      <LeftAgents className={cls.sider} manager={props.manager} />
      <ContentBody manager={props.manager} />
    </Layout>
  );
};

const ContentBody = (props: IProps) => {
  const [current] = useCurrent(props.manager);
  if (current) {
    return (
      <Layout key={current.id} className={cls.content}>
        <ChatHeader className={cls.header} current={current} />
        <ChatBody className={cls.body} current={current} />
        <ChatBox className={cls.box} current={current} />
      </Layout>
    );
  }
  return <Welcome manager={props.manager} />;
};

const Welcome = (props: IProps) => {
  const [tips] = useState([
    '如何提高工作效率？',
    '请用简单的语言解释量子计算',
    '给我写一个Python爬虫示例',
  ]);
  return (
    <Layout className={cls.content}>
      <div className={cls.welcome}>
        <div className={cls.welcomeTitle}>欢迎使用奧能小智</div>
        <div className={cls.welcomeSubtitle}>我可以帮助您解决各种问题</div>
        <div className={cls.welcomeExamples}>
          <div className={cls.exampleGroup}>
            <div className={cls.exampleTitle}>示例问题</div>
            {tips.map((tip, index) => {
              return (
                <div
                  className={cls.exampleItem}
                  key={index}
                  onClick={() => {
                    props.manager.createAgent({ name: tip } as schema.XAgent, true);
                  }}>
                  {tip}
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </Layout>
  );
};

interface ManagerProps extends IProps {
  className: string;
}

const LeftAgents = (props: ManagerProps) => {
  const [loading, setLoading] = useState(false);
  const [items, setItems] = useState<IAgent[]>([]);
  const [current] = useCurrent(props.manager);
  const loadAgents = async () => {
    setLoading(true);
    await props.manager.loadCurAgent();
    setItems([...props.manager.agents]);
    props.manager.command.emitter('agent', 'selected');
    setLoading(false);
  };
  useEffect(() => {
    loadAgents();
    const id = props.manager.command.subscribe(async (type, cmd) => {
      switch (type) {
        case 'agent':
          switch (cmd) {
            case 'create': {
              await loadAgents();
              break;
            }
          }
      }
    });
    return () => {
      props.manager.unsubscribe(id);
    };
  }, []);
  return (
    <Sider width={276} collapsible collapsed={false} trigger={<></>} defaultCollapsed>
      <div className={props.className}>
        <div className={cls.collapsed}>
          <div className={cls.agentIcon}>
            <OrgIcons type={`/topbar/smart`} selected size={36} />
            <div className={cls.agentName}>奧能小智</div>
          </div>
        </div>
        <div
          className={cls.addAgent}
          onClick={() => command.emitter('executor', 'addAgent', props.manager)}>
          <OrgIcons type={'/operate/addAgent'} size={24} />
          <div className={cls.addName}>新建话题</div>
        </div>
        <Spin spinning={loading}>
          <Menu
            selectedKeys={current ? [current.id] : []}
            items={Object.entries(grouping(items)).map(([group, items]) => {
              return {
                key: group,
                label: group,
                type: 'group',
                children: items.map((item) => {
                  return {
                    key: item.id,
                    label: item.name,
                    icon: <OrgIcons type={`/operate/agentChat`} size={24} />,
                    onClick: () => {
                      props.manager.setCurAgent(item);
                      props.manager.command.emitter('agent', 'selected');
                    },
                  };
                }),
              };
            })}
          />
        </Spin>
      </div>
    </Sider>
  );
};

interface CompProps {
  current: IAgent;
  className: string;
}

const useCurrent = (manager: IAgentManager) => {
  const [current, setCurrent] = useState<IAgent>();
  useEffect(() => {
    const id = manager.command.subscribe((type, cmd) => {
      switch (type) {
        case 'agent':
          switch (cmd) {
            case 'selected': {
              setCurrent(manager.curAgent);
              break;
            }
          }
      }
    });
    return () => {
      manager.command.unsubscribe(id);
    };
  }, []);
  return [current];
};

const ChatHeader = (props: CompProps) => {
  return (
    <Header className={props.className}>
      <div className={cls.title}>
        <div>
          <OrgIcons type={`/operate/agentChat`} size={36} />
        </div>
        <div className={cls.font}>{props.current.name}</div>
      </div>
      <div className={cls.tools}>
        <div className={cls.btn}>
          <OrgIcons type={'/operate/agentTips'} size={16} />
          提示
          <OrgIcons type={'/operate/arrowDown'} size={16} />
        </div>
        <div className={cls.btn}>
          <OrgIcons type={'/operate/agentTips'} size={16} />
          插件
          <OrgIcons type={'/operate/arrowDown'} size={16} />
        </div>
      </div>
    </Header>
  );
};

interface LoadMessage {
  loadType: 'onMessage' | 'loadMore';
  data: IUpdatableMessage[];
}

interface TokensProps {
  className: string;
  current: IAgent;
  message: IUpdatableMessage;
}

const Tokens = (props: TokensProps) => {
  const [status, setStatus] = useState<TokenStatus>(
    props.message.metadata.status as TokenStatus,
  );
  const [content, setContent] = useState(props.message.msgBody as any);
  useEffect(() => {
    const id = props.current.command.subscribe((type, cmd, args) => {
      if (props.message.id !== args?.message?.id) {
        return;
      }
      switch (type) {
        case 'tokens':
          switch (cmd) {
            case 'event':
              const { status, content } = args as StreamResponse;
              setStatus(status);
              setContent(content);
              break;
          }
          break;
      }
    });
    return () => {
      props.current.command.unsubscribe(id);
    };
  }, []);
  switch (status) {
    case TokenStatus.thinking:
      return (
        <Space>
          思考中
          <LoadingOutlined />
        </Space>
      );
    case TokenStatus.error:
      return (
        <Space>
          <CloseSquareOutlined />
          生成异常：{content}
        </Space>
      );
    default: {
      const body = content as any as BodyContent;
      return (
        <div className={cls.tokens}>
          <div className={cls.reasoning}>{body.reasoning?.trim()}</div>
          {body.content && <div className={props.className}>{body.content?.trim()}</div>}
        </div>
      );
    }
  }
};

const ChatBody = (props: CompProps) => {
  const [loading, setLoading] = useState(false);
  const [messages, setMessages] = useState<LoadMessage>({
    loadType: 'onMessage',
    data: [],
  });
  const preScrollHeight = useRef(0);
  const userScrolled = useRef(false);

  const bottomRef = useRef<HTMLDivElement>(null);
  const contentRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const id = props.current.command.subscribe((type, cmd) => {
      switch (type) {
        case 'send':
          switch (cmd) {
            case 'before': {
              userScrolled.current = false;
              break;
            }
          }
      }
    });
    return () => {
      props.current.command.unsubscribe(id);
    };
  }, []);

  useEffect(() => {
    userScrolled.current = false;
    props.current.onMessage((messages) => {
      setLoading(true);
      setMessages({ loadType: 'onMessage', data: messages });
      setLoading(false);
    });
    return () => {
      props.current.unMessage();
    };
  }, []);

  const onMessage = () => {
    if (userScrolled.current) {
      return;
    }
    bottomRef.current?.scrollIntoView();
  };

  useLayoutEffect(() => {
    switch (messages.loadType) {
      case 'onMessage':
        onMessage();
        break;
      case 'loadMore':
        if (contentRef.current) {
          const scrollHeight = contentRef.current.scrollHeight;
          contentRef.current.scrollTop = scrollHeight - preScrollHeight.current;
        }
        break;
    }
  }, [messages]);

  useEffect(() => {
    const container = contentRef.current;
    if (container) {
      const onScroll = () => {
        if (contentRef?.current?.scrollTop == 0) {
          preScrollHeight.current = contentRef.current.scrollHeight;
          props.current.loadMores().then((data) => {
            setMessages({ loadType: 'loadMore', data });
          });
        }
      };
      const onWheel = () => (userScrolled.current = true);
      container.addEventListener('wheel', onWheel);
      container.addEventListener('scroll', onScroll);
      return () => {
        container.addEventListener('wheel', onWheel);
        container.removeEventListener('scroll', onScroll);
      };
    }
  }, []);

  return (
    <Spin spinning={loading} className={cls.spinning}>
      <Content ref={contentRef} className={props.className}>
        {messages.data.map((item) => {
          if (item.msgType == MessageType.Tokens) {
            return (
              <Tokens
                key={item.id}
                className={cls.left}
                current={props.current}
                message={item}
              />
            );
          }
          return (
            <div key={item.id} className={cls.right}>
              {parseMsg(item)}
            </div>
          );
        })}
        <div ref={bottomRef} />
      </Content>
    </Spin>
  );
};

const ModelSelector = (props: CompProps) => {
  const [model, setModel] = useState(
    props.current.manager.curModel ?? props.current.target.modelManager.defaultModel,
  );
  const [modelLoading, setModelLoading] = useState(false);
  const [models, setModels] = useState(props.current.target.modelManager.models);
  const initialize = async () => {
    setModelLoading(true);
    const model = await props.current.manager.loadCurModel();
    setModels([...props.current.target.modelManager.models]);
    setModel(model ?? props.current.target.modelManager.defaultModel);
    setModelLoading(false);
  };
  useEffect(() => {
    initialize();
  }, []);
  const Content = () => {
    const content: ReactNode[] = [];
    if (model?.deepThink) {
      content.push(
        <div key={'deepThing'} className={cls.think}>
          <OrgIcons type={'/operate/brain'} size={16} />
          深度思考
        </div>,
      );
    }
    if (model?.netSearch) {
      content.push(
        <div key={'search'} className={cls.think}>
          <OrgIcons type={'/operate/globe'} size={16} />
          联网搜索
        </div>,
      );
    }
    return content;
  };

  return (
    <div className={cls.model}>
      <Select
        value={model?.id ?? props.current.manager.target.modelManager.defaultModel.id}
        className={cls.select}
        loading={modelLoading}
        onChange={(value) => {
          const model = models.find((item) => {
            return item.id == value;
          });
          if (model) {
            setModel(model);
            props.current.manager.setCurModel(model);
          }
        }}
        options={models.map((item) => {
          return {
            label: `${item.code}（${item.protocol}）`,
            value: item.id,
          };
        })}
      />
      <Content />
    </div>
  );
};

const ChatBox = (props: CompProps) => {
  const [content, setContent] = useState(props.current.content);
  const [status, setStatus] = useState(props.current.status);

  useEffect(() => {
    if (props.current.task) {
      handleSend(props.current.name);
      props.current.task = false;
    }
    props.current.onStatusChanged = async (status: TokenStatus) => {
      setStatus(status);
    };
    return () => {
      props.current.onStatusChanged = undefined;
    };
  }, []);

  const contentSync = (input: string) => {
    props.current.content = input;
    setContent(props.current.content);
  };

  const handleSend = async (input: string) => {
    if (!input) {
      message.warning('请输入感兴趣的话题吧~');
      return;
    }
    const model =
      props.current.manager.curModel ?? props.current.target.modelManager.defaultModel;

    props.current.command.emitter('send', 'before');

    props.current.status = TokenStatus.thinking;
    setStatus(TokenStatus.thinking);
    props.current.aborter = new AbortController();
    const sended = await props.current.sendMessage({
      type: MessageType.Text,
      content: input,
    });
    if (!sended) {
      message.error('发送失败');
      return;
    }
    contentSync('');

    const result = await props.current.backMessage();
    if (!result) {
      message.error('生成智能体消息失败');
      return;
    }
    await props.current.genModelTokens(model, {
      content: input,
      message: result,
      onStart: async () => {
        await common.sleep(50);
        props.current.command.emitter('tokens', 'start');
      },
      onEvent: async (response) => {
        props.current.command.emitter('tokens', 'event', response);
      },
    });
  };

  const ButtonBody = (props: { status: TokenStatus }) => {
    switch (props.status) {
      case TokenStatus.generating: {
        return (
          <div className={cls.send}>
            <CloseSquareOutlined />
            终止
          </div>
        );
      }
      default:
        return (
          <div className={cls.send}>
            <OrgIcons className={cls.icon} type={'/operate/send'} size={16} />
            发送
          </div>
        );
    }
  };

  return (
    <Footer className={cls.box}>
      <Input
        value={content}
        className={cls.input}
        placeholder="有问题尽管问我"
        onPressEnter={() => handleSend(props.current.content)}
        onChange={(e) => contentSync(e.target.value)}
      />
      <div className={cls.tools}>
        <ModelSelector current={props.current} className={cls.model} />
        <div className={cls.operate}>
          <OrgIcons type={'/toolbar/audio'} size={16} />
          <OrgIcons type={'/toolbar/store'} size={16} />
          <Button
            loading={status == TokenStatus.thinking}
            className={cls.btn}
            onClick={() => {
              if (status == TokenStatus.generating) {
                props.current.aborter?.abort();
                return;
              }
              handleSend(props.current.content);
            }}>
            <ButtonBody status={status} />
          </Button>
        </div>
      </div>
    </Footer>
  );
};

const grouping = (items: IAgent[]) => {
  const today = new Date().setHours(0, 0, 0, 0);
  const weekAgo = new Date(today - 7 * 24 * 60 * 60 * 1000).getTime();
  return items
    .sort((pre, next) => next.metadata.createTime.localeCompare(pre.metadata.createTime))
    .reduce((pre, next) => {
      const date = new Date(next.metadata.createTime);
      const dateStr = `${date.getMonth() + 1}月${date.getDate()}日`;
      if (date.getTime() > today) {
        pre['今天'] = pre['今天'] || [];
        pre['今天'].push(next);
      } else if (date.getTime() > weekAgo) {
        pre[dateStr] = pre[dateStr] || [];
        pre[dateStr].push(next);
      } else {
        pre['更早'] = pre['更早'] || [];
        pre['更早'].push(next);
      }
      return pre;
    }, {} as Record<string, IAgent[]>);
};

export default AiAgent;
